package frp

import (
	"context"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"net"
	"time"

	"gitee.com/baixudong/gospider/chanx"
	"gitee.com/baixudong/gospider/thread"
)

type Client struct {
	rpcCli     *rpc
	serverConn net.Conn
	pool       *thread.Client
	ctx        context.Context
	cnl        context.CancelFunc
	chost      string //客户端地址
	shost      string //服务端地址
}
type Server struct {
	server net.Listener
	usr    string //用户名
	pwd    string //密码
	pool   *thread.Client
}
type ClientOption struct {
	Server     ServerOption //服务器参数
	Host       string       //本地的host
	Port       int          //本地的port
	RemotePort int          //外网映射端口
}
type ServerOption struct {
	Host string
	Port int
	Usr  string
	Pwd  string
}
type serverVerifyOption struct {
	Usr  string `json:"usr"`
	Pwd  string `json:"pwd"`
	Port int    `json:"port"`
}
type state = int

var (
	createState state = 1
	errState    state = 2
	okState     state = 3
	pingState   state = 4
)

type recvData struct {
	State state  `json:"state"` //是否成功
	Err   string `json:"err"`   //错误信息
}

func NewClient(preCtx context.Context, option ClientOption) (*Client, error) {
	if option.RemotePort == 0 {
		option.RemotePort = option.Port
	}
	if option.Port == 0 {
		return nil, fmt.Errorf("端口错误")
	}
	serverConn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", option.Server.Host, option.Server.Port))
	if err != nil {
		return nil, err
	}
	chost := fmt.Sprintf("%s:%d", option.Host, option.Port)
	shost := fmt.Sprintf("%s:%d", option.Server.Host, option.RemotePort)

	clientConn, err := net.Dial("tcp", chost)
	if err != nil {
		return nil, err
	}
	err = clientConn.Close()
	if err != nil {
		return nil, err
	}
	var client Client
	ctx, cnl := context.WithCancel(preCtx)
	client.serverConn = serverConn
	client.rpcCli = newRpc(serverConn)
	client.pool = thread.NewClient(ctx, 65535)
	client.ctx = ctx
	client.cnl = cnl
	client.chost = chost
	client.shost = shost
	return &client, client.rpcCli.clientVerify(serverVerifyOption{Usr: option.Server.Usr, Pwd: option.Server.Pwd, Port: option.RemotePort})
}
func (obj *Client) close() error {
	obj.pool.Close()
	obj.cnl()
	return obj.serverConn.Close()
}
func (obj *Client) newConn(preCtx context.Context) error {
	var clientConn, serverConn net.Conn
	var clientErr, serverErr error
	ctx, cnl := context.WithCancel(preCtx)
	go func() {
		defer cnl()
		clientConn, clientErr = obj.newConnTry(preCtx, obj.chost)
	}()
	serverConn, serverErr = obj.newConnTry(preCtx, obj.shost)
	<-ctx.Done()
	if clientErr == nil {
		defer clientConn.Close()
	}
	if serverErr == nil {
		defer serverConn.Close()
	}
	if clientErr != nil {
		return clientErr
	}
	if serverErr != nil {
		return serverErr
	}
	go io.Copy(clientConn, serverConn)
	_, err := io.Copy(serverConn, clientConn)
	return err
}

func (obj *Client) newConnTry(ctx context.Context, host string) (net.Conn, error) {
	var clientConn net.Conn
	var err error
	for i := 0; i < 3; i++ {
		select {
		case <-ctx.Done():
		default:
			clientConn, err = net.Dial("tcp", host)
			if err == nil {
				return clientConn, err
			}
		}
	}
	return clientConn, err
}

func (obj *Client) Run() error {
	defer obj.close()
	for {
		var data recvData
		obj.rpcCli.recvConn.Decode(&data)
		switch data.State {
		case pingState, okState:
		case errState:
			return fmt.Errorf(data.Err)
		case createState:
			obj.pool.Write(&thread.Task{
				Func: obj.newConn,
			})

		}
	}
}

func NewServer(ctx context.Context, option ServerOption) (*Server, error) {
	var server Server
	listen, err := net.Listen("tcp", fmt.Sprintf("%s:%d", option.Host, option.Port))
	server.server = listen
	server.pool = thread.NewClient(ctx, 65535)
	return &server, err
}

func (obj *Server) Run() error {
	defer obj.pool.Close()
	for {
		conn, err := obj.server.Accept()
		if err != nil {
			return err
		}
		_, err = obj.pool.Write(&thread.Task{
			Func: obj.newServer,
			Args: []any{conn},
		})
		if err != nil {
			return err
		}
	}
}

type transferServer struct {
	listen net.Listener
	srcIp  string
	ctx    context.Context
	cnl    context.CancelFunc
	rpcCli *rpc
	err    error
	conns  *chanx.Client[net.Conn]
	pool   *thread.Client
}

func newTransferServer(preCtx context.Context, rpcCli *rpc, srcIp string, port int) (*transferServer, error) {
	var tranServer transferServer
	var err error
	tranServer.listen, err = net.Listen("tcp", fmt.Sprintf("0.0.0.0:%d", port))
	if err != nil {
		return nil, err
	}
	tranServer.srcIp = srcIp
	ctx, cnl := context.WithCancel(preCtx)
	tranServer.ctx = ctx
	tranServer.cnl = cnl
	tranServer.rpcCli = rpcCli
	tranServer.conns = chanx.NewClient[net.Conn](ctx)
	tranServer.pool = thread.NewClient(ctx, 65535)
	return &tranServer, err
}
func (obj *transferServer) close() {
	obj.pool.Close()
	obj.listen.Close()
	obj.cnl()
}
func (obj *transferServer) done() <-chan struct{} {
	return obj.ctx.Done()
}
func (obj *transferServer) newConn(ctx context.Context, conn net.Conn) error {
	defer conn.Close()
	if err := obj.rpcCli.create(); err != nil {
		return err
	}
	select {
	case srcCon := <-obj.conns.Chan():
		defer srcCon.Close()
		go io.Copy(conn, srcCon)
		_, err := io.Copy(srcCon, conn)
		return err
	case <-time.After(time.Second * 15): //超时退出
		return fmt.Errorf("timeout")
	}
}
func (obj *transferServer) run() error {
	defer obj.close()
	for {
		conn, err := obj.listen.Accept()
		if err != nil {
			obj.err = err
			return err
		}
		srcIp, _, err := net.SplitHostPort(conn.RemoteAddr().String())
		if err != nil {
			obj.err = err
			return err
		}
		if obj.srcIp == srcIp {
			obj.conns.Add(conn)
		} else {
			_, err = obj.pool.Write(&thread.Task{
				Func: obj.newConn,
				Args: []any{conn},
			})
			if err != nil {
				obj.err = err
				return err
			}
		}
	}
}
func (obj *transferServer) ping() error {
	defer obj.close()
	for {
		time.Sleep(time.Second * 10)
		if err := obj.rpcCli.ping(); err != nil {
			return err
		}
	}
}

type rpc struct {
	recvConn *json.Decoder
	sendConn *json.Encoder
}

func newRpc(conn net.Conn) *rpc {
	return &rpc{
		recvConn: json.NewDecoder(conn),
		sendConn: json.NewEncoder(conn),
	}
}
func (obj *rpc) serverVerify(usr, pwd string) (serverVerifyOption, error) {
	var verifyOption serverVerifyOption
	if err := obj.recvConn.Decode(&verifyOption); err != nil {
		return verifyOption, err
	}
	if verifyOption.Usr != usr || verifyOption.Pwd != pwd || verifyOption.Port == 0 {
		return verifyOption, errors.New("verify false")
	}
	return verifyOption, nil
}
func (obj *rpc) clientVerify(verifyOption serverVerifyOption) error {
	if err := obj.sendConn.Encode(verifyOption); err != nil {
		return err
	}
	var data recvData
	if err := obj.recvConn.Decode(&data); err != nil {
		return err
	}
	switch data.State {
	case okState:
		return nil
	case errState:
		return errors.New(data.Err)
	default:
		return fmt.Errorf("%v", data)
	}
}
func (obj *rpc) error(err error) error {
	if enErr := obj.sendConn.Encode(recvData{
		State: errState,
		Err:   err.Error(),
	}); enErr != nil {
		return enErr
	}
	return err
}
func (obj *rpc) ok() error {
	return obj.sendConn.Encode(recvData{
		State: okState,
	})
}
func (obj *rpc) ping() error {
	return obj.sendConn.Encode(recvData{
		State: pingState,
	})
}
func (obj *rpc) create() error {
	return obj.sendConn.Encode(recvData{
		State: createState,
	})
}

func (obj *Server) newServer(ctx context.Context, conn net.Conn) error {
	defer conn.Close()
	rpcCli := newRpc(conn)
	verifyOption, err := rpcCli.serverVerify(obj.usr, obj.pwd)
	if err != nil {
		return rpcCli.error(err)
	}
	srcIp, _, err := net.SplitHostPort(conn.RemoteAddr().String())
	//开始验证参数
	if err != nil {
		return rpcCli.error(err)
	}

	transferServer, err := newTransferServer(ctx, rpcCli, srcIp, verifyOption.Port)
	if err != nil {
		return rpcCli.error(err)
	}
	defer transferServer.close()
	go transferServer.run()
	if err = rpcCli.ok(); err != nil {
		return err
	}
	go transferServer.ping()
	<-transferServer.done()
	return err
}
